#include <Hazel.h>
#include "Hazel/Core/EntryPoint.h"

#include "Hazel/Renderer/Camera.h"
#include "Platform/OpenGL/OpenGLShader.h"

#include "imgui/imgui.h"

#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

class ExampleLayer : public Hazel::Layer
{
public:

	void imguidockerspace()
	{
		static bool dockingEnabled = true;
		if (dockingEnabled)
		{
			static bool dockspaceOpen = true;
			static bool opt_fullscreen_persistant = true;
			bool opt_fullscreen = opt_fullscreen_persistant;
			static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None;

			// We are using the ImGuiWindowFlags_NoDocking flag to make the parent window not dockable into,
			// because it would be confusing to have two docking targets within each others.
			ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking;
			if (opt_fullscreen)
			{
				ImGuiViewport* viewport = ImGui::GetMainViewport();
				ImGui::SetNextWindowPos(viewport->Pos);
				ImGui::SetNextWindowSize(viewport->Size);
				ImGui::SetNextWindowViewport(viewport->ID);
				ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
				ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
				window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove;
				window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus;
			}

			// When using ImGuiDockNodeFlags_PassthruCentralNode, DockSpace() will render our background and handle the pass-thru hole, so we ask Begin() to not render a background.
			if (dockspace_flags & ImGuiDockNodeFlags_PassthruCentralNode)
				window_flags |= ImGuiWindowFlags_NoBackground;

			// Important: note that we proceed even if Begin() returns false (aka window is collapsed).
			// This is because we want to keep our DockSpace() active. If a DockSpace() is inactive, 
			// all active windows docked into it will lose their parent and become undocked.
			// We cannot preserve the docking relationship between an active window and an inactive docking, otherwise 
			// any change of dockspace/settings would lead to windows being stuck in limbo and never being visible.
			ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
			ImGui::Begin("DockSpace Demo", &dockspaceOpen, window_flags);
			ImGui::PopStyleVar();

			if (opt_fullscreen)
				ImGui::PopStyleVar(2);

			// DockSpace
			ImGuiIO& io = ImGui::GetIO();
			if (io.ConfigFlags & ImGuiConfigFlags_DockingEnable)
			{
				ImGuiID dockspace_id = ImGui::GetID("MyDockSpace");
				ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
			}

			if (ImGui::BeginMenuBar())
			{
				if (ImGui::BeginMenu("File"))
				{
					// Disabling fullscreen would allow the window to be moved to the front of other windows, 
					// which we can't undo at the moment without finer window depth/z control.
					//ImGui::MenuItem("Fullscreen", NULL, &opt_fullscreen_persistant);

					if (ImGui::MenuItem("Exit")) Hazel::Application::Get().Close();
					ImGui::EndMenu();
				}

				ImGui::EndMenuBar();
			}
			ImGui::End();
		}
	}

	ExampleLayer()
		: Layer("Example"), m_Camera(45.0f, 0.1f, 100.0f)
	{
		Hazel::Material& pinkSphere = m_Scene.Materials.emplace_back();
		pinkSphere.Albedo = { 1.0f,0.0f,1.0f };
		pinkSphere.Roughness = 0.0f;
		Hazel::Material& blueSphere = m_Scene.Materials.emplace_back();
		blueSphere.Albedo = { 0.2f,0.3f,0.8f };
		blueSphere.Roughness = 0.1f;		
		Hazel::Material& orangeSphere = m_Scene.Materials.emplace_back();
		orangeSphere.Albedo = { 0.8f,0.5f,0.2f };
		orangeSphere.Roughness = 0.1f;
		orangeSphere.EmissionColor = orangeSphere.Albedo;
		orangeSphere.EmissionPower = 2.0f;

		{
			sphere.Position = { 0.0f,0.0f,0.0f };
			sphere.Radius = 1.0f;
			sphere.MaterialIndex = 0;

			m_Scene.Spheres.push_back(sphere);

		}
		{
			sphere.Position = { 2.0f,0.0f,0.0f };
			sphere.Radius = 1.0f;
			sphere.MaterialIndex = 2;

			m_Scene.Spheres.push_back(sphere);

		}
		{
			sphere.Position = { 0.0f, -101.0f,0.0f };
			sphere.Radius = 100.0f;
			sphere.MaterialIndex = 1;

			m_Scene.Spheres.push_back(sphere);
		}
	}
	virtual void OnUpdate(Hazel::Timestep ts) override
	{
		if (m_Camera.OnUpdate(ts.GetSeconds()))
			m_Renderer.ResetFrameIndex();
	}
	virtual void OnAttach() override
	{

	}
	virtual void OnImGuiRender() override
	{
		imguidockerspace();
		ImGui::Begin("Settings");
		ImGui::Text("Last Render Time: %.3fms", m_LastRenderTime);
		ImGui::Text("Last Mouse Position: %.3f %.3f", m_Camera.GetMouseLastPosition().x, m_Camera.GetMouseLastPosition().y);
		if (ImGui::Button("Render"))
		{
			Render();
		}

		ImGui::Checkbox("Accumulate", &m_Renderer.GetSettings().Accumulate);

		if (ImGui::Button("ReSize"))
		{
			m_Renderer.ResetFrameIndex();
		}
		ImGui::End();

		ImGui::Begin("Scene");
		for (size_t i = 0; i < m_Scene.Spheres.size(); i++)
		{
			ImGui::PushID(i);
			ImGui::DragFloat3("Position", glm::value_ptr(m_Scene.Spheres[i].Position), 0.1f);
			ImGui::DragFloat("Radius", &(m_Scene.Spheres[i].Radius), 0.1f);
			ImGui::DragInt("Material", &(m_Scene.Spheres[i].MaterialIndex), 1.0f, 0, (int)m_Scene.Materials.size() - 1);

			ImGui::Separator();

			ImGui::PopID();
		}
		for (size_t i = 0; i < m_Scene.Materials.size(); i++)
		{
			ImGui::PushID(i);
			ImGui::ColorEdit3("Albedo", glm::value_ptr(m_Scene.Materials[i].Albedo));
			ImGui::DragFloat("Roughness", &(m_Scene.Materials[i].Roughness), 0.05f, 0.0f, 1.0f);
			ImGui::DragFloat("Metallic", &(m_Scene.Materials[i].Metallic), 0.05f, 0.0f, 1.0f);
			ImGui::DragFloat("EmissionPower", &(m_Scene.Materials[i].EmissionPower), 0.05f, 0.0f, FLT_MAX);
			ImGui::ColorEdit3("EmissionColor", glm::value_ptr(m_Scene.Materials[i].EmissionColor));

			ImGui::Separator();
			ImGui::PopID();

		}
		ImGui::End();

		ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{ 0, 0 });
		ImGui::Begin("viewport");
		m_ViewportHeight = ImGui::GetContentRegionAvail().x;
		m_ViewportWidth = ImGui::GetContentRegionAvail().y;
		auto image = m_Renderer.GetFinalImage();
		if (image != nullptr)
		{

			uint32_t textureID = image->GetRendererID();
			ImGui::Image((ImTextureID)textureID, ImVec2(image->GetHeight(), image->GetWidth()), ImVec2(0, 1), ImVec2(1, 0));
		}
		ImGui::End();
		ImGui::PopStyleVar();

		Render();

	}

	void Render()
	{
		Hazel::Timer timer("render");

		m_Renderer.OnResize(m_ViewportWidth, m_ViewportHeight);
		m_Camera.OnResize(m_ViewportWidth, m_ViewportHeight);
		m_Renderer.Render(m_Scene, m_Camera);

		m_LastRenderTime = timer.GetTime();
	}
private:
	uint32_t m_ViewportWidth = 0, m_ViewportHeight = 0;
	Hazel::Renderer m_Renderer;
	Hazel::Camera m_Camera;
	Hazel::Scene m_Scene;
	Hazel::Sphere sphere;

	double m_LastRenderTime;

};

class Sandbox : public Hazel::Application
{
public:
	Sandbox()
	{
		PushLayer(new ExampleLayer());
	}

	~Sandbox()
	{

	}

};

Hazel::Application* Hazel::CreateApplication()
{
	return new Sandbox();
}